package com.gao.d_selector.c;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/*
	针对于b包中“cpu有很大的几率在空转”的这个问题，使用了Selector对象来解决。
	Selector的select()方法是阻塞的，直到其管理的多个socket中的某些socket
	有事件发生了，才会返回。

	注意，该例子中，服务端是非阻塞的，而客户端是阻塞的
*/

public class Server {
	public static void main(String[] args) {
		try {
			ServerSocketChannel ssc = ServerSocketChannel.open();
			ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8000));
			System.out.println("server has been put up");
			// 以下代码，等价于设置accpet为非阻塞的
			ssc.configureBlocking(false);
			Selector selector = Selector.open();
			ssc.register(selector, SelectionKey.OP_ACCEPT);
			ByteBuffer readBuff = ByteBuffer.allocate(1024);
			ByteBuffer writeBuff = ByteBuffer.allocate(128);
			writeBuff.put("received".getBytes());
			writeBuff.flip();
			while (true) {
				// Selector的select方法是阻塞的，底层使用的是linux的epoll函数
				int nReady = selector.select();
				Set<SelectionKey> keys = selector.selectedKeys();

				Iterator<SelectionKey> it = keys.iterator();

				while (it.hasNext()) {
					SelectionKey key = it.next();
					it.remove();
					
					if (key.isAcceptable()) {
						System.out.println("new client connect");
						SocketChannel socketChannel = ssc.accept();
						// 以下代码，等价于设置read为非阻塞的
						socketChannel.configureBlocking(false);
						socketChannel.register(selector, SelectionKey.OP_READ);
					} else if (key.isReadable()) {
						SocketChannel socketChannel = (SocketChannel) key.channel();
						readBuff.clear();
						socketChannel.read(readBuff);
						readBuff.flip();
						System.out.println("server received: " + new String(readBuff.array()));
						key.interestOps(SelectionKey.OP_WRITE);
					} else if (key.isWritable()) {
						writeBuff.rewind();
						SocketChannel socketChannel = (SocketChannel) key.channel();
						socketChannel.write(writeBuff);
						key.interestOps(SelectionKey.OP_READ);
					}
				}
			} 
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
